
#include "cvf_ImageFilter.h"

// [*] CImageFilter

CImageFilter::CImageFilter()
{
	Clear_IO_Relationship();
}

CImageFilter::~CImageFilter()
{
	//TODO
}

void CImageFilter::Clear_IO_Relationship()
{
	_SelfIndex = -1;
	_OutputOffset = -1;

	_InputDimension = 0;
	InputFilterIndexList.Clear();
	InputResponseIndexList.Clear();
	_OutputDimension = 0;
	OutputIndexList.Clear();

	AreaThresholdForResponseCacheUsage = -1;

	_ResponseUserAmount = 0;
	ClearContent();
}

void CImageFilter::Copy_IO_Settings(const CImageFilter* pImageFilterPrototype)
{
	_SelfIndex = pImageFilterPrototype->_SelfIndex;
	_OutputOffset = pImageFilterPrototype->_OutputOffset;

	_InputDimension = pImageFilterPrototype->_InputDimension;
	InputFilterIndexList = pImageFilterPrototype->InputFilterIndexList;
	InputResponseIndexList = pImageFilterPrototype->InputResponseIndexList;
	_OutputDimension = pImageFilterPrototype->_OutputDimension;
	OutputIndexList = pImageFilterPrototype->OutputIndexList;

	AreaThresholdForResponseCacheUsage = pImageFilterPrototype->AreaThresholdForResponseCacheUsage;

	_ResponseUserAmount = pImageFilterPrototype->_ResponseUserAmount;
	ClearContent();
}

void CImageFilter::SetSelfIndex(int SelfIndex)
{
	_SelfIndex = SelfIndex;
}

int CImageFilter::GetSelfIndex()
{
	return _SelfIndex;
}

void CImageFilter::SetOutputOffset(int OutputOffset)
{
	_OutputOffset = OutputOffset;
}

int CImageFilter::GetInputDimension()
{
	return _InputDimension;
}

int CImageFilter::GetOutputDimension()
{
	return _OutputDimension;
}

void CImageFilter::PushBackInputResponse(CImageFilter* pFilter, int ResponseIndex)
{
	++_InputDimension;
	++pFilter->_ResponseUserAmount;
	InputFilterIndexList.PushBack(pFilter->GetSelfIndex());
	InputResponseIndexList.PushBack(ResponseIndex);
}

void CImageFilter::PushBackOutput(int ResponseIndex)
{
	++_OutputDimension;
	OutputIndexList.PushBack(ResponseIndex);
}

size_t CImageFilter::GetMemoryUsage() const
{
	return sizeof(int) * 6 + sizeof(bool)
		+ sizeof(AreaThresholdForResponseCacheUsage)
		+ InputFilterIndexList.GetMemoryUsage()
		+ InputResponseIndexList.GetMemoryUsage()
		+ OutputIndexList.GetMemoryUsage()
		+ ResponseList.GetMemoryUsage(); 
}

//bool CImageFilter::IsCalculationDone()
//{
//	return (_ReportedChildrenAmount >= 0);
//}

void CImageFilter::ClearContent()
{
	ResponseList.DeepClear();
	
	fIsCalculationDone = false;
	_Amount_ReportGot = 0;
	
	//_ReportedChildrenAmount = -1;
}

void CImageFilter::MakeFN_OutputCache(char* DstChar, const char* ccpPN_Cache, const char* ccpFN_ImageHash)
{
	sprintf(DstChar, "%s%s.%d.dat", ccpPN_Cache, ccpFN_ImageHash, _SelfIndex);
}

void CImageFilter::Work(
	int W, int H, CImageFilter** ppInputFilterListBase, double* pOutput,
	const char* ccpPN_Cache, const char* ccpFN_ImageHash)
{
	if (fIsCalculationDone || ppInputFilterListBase==NULL) return;
	//if (IsCalculationDone() || ppInputFilterListBase==NULL) return;

	int r_dim = GetResponseDimension();
	int i_dim = GetInputDimension();
	int o_dim = GetOutputDimension();
	int WH = W * H;
	size_t WHR = ((size_t)WH) * (size_t)r_dim;

	int i, j, fi, ri;
	int* pFilterIndex;
	int* pResponseIndex;

	ResponseList.Resize(WHR);
	
	_Amount_ReportGot = 0;
	//_ReportedChildrenAmount = -1;

	bool fCacheLoaded = false;
	if (ccpPN_Cache && ccpFN_ImageHash && *ccpPN_Cache && *ccpFN_ImageHash
		&& AreaThresholdForResponseCacheUsage>=0
		&& AreaThresholdForResponseCacheUsage<=WH
		)
	{
		char strTemp[10001];
		MakeFN_OutputCache(strTemp, ccpPN_Cache, ccpFN_ImageHash);
		FILE* FIN = fopen(strTemp, "rb");
		if (FIN != NULL)
		{
			int iw, ih, ii, ir;
			iw = ih = -1;
			fread(&iw, sizeof(iw), 1, FIN);
			fread(&ih, sizeof(ih), 1, FIN);
			fread(&ii, sizeof(ii), 1, FIN);
			fread(&ir, sizeof(ir), 1, FIN);
			if (iw==W && ih==H && ii==i_dim && ir==r_dim)
			{
				float* FD = new float[WHR];
				fread(FD, sizeof(*FD), WHR, FIN);
				double* pdd = ResponseList.pElement;
				const float* qfd = FD;
				size_t st_i;
				for ( st_i = 0; st_i < WHR; ++st_i )
					*(pdd++) = *(qfd++);
				delete[] FD;

				fCacheLoaded = true;
			}
			fclose(FIN);
		}
	}

	// Simply ask previous filters to work, no matter whether "fCacheLoaded"
	// Otherwise may cause confusion for previous filters
	CSimpleTypeArray<double*> InputResponsePtrList;
	InputResponsePtrList.Clear();
	pFilterIndex = InputFilterIndexList.pElement;
	pResponseIndex = InputResponseIndexList.pElement;
	for ( i = 0; i < i_dim; ++i )
	{
		fi = *(pFilterIndex++);
		ppInputFilterListBase[fi]->Work(W, H, ppInputFilterListBase, pOutput, ccpPN_Cache, ccpFN_ImageHash);
		ri = *(pResponseIndex++);
		InputResponsePtrList.PushBack(ppInputFilterListBase[fi]->ResponseList.pElement + WH * ri);
	}

	if (!fCacheLoaded)
	{
		ResponseList.Fill(0.0);

		_ComputeResponse(W, H, InputResponsePtrList.pElement, ResponseList.pElement);

		if (ccpPN_Cache && ccpFN_ImageHash && *ccpPN_Cache && *ccpFN_ImageHash
			&& AreaThresholdForResponseCacheUsage>=0
			&& AreaThresholdForResponseCacheUsage<=WH
			)
		{
			char strTemp[10001];
			MakeFN_OutputCache(strTemp, ccpPN_Cache, ccpFN_ImageHash);
			FILE* FOUT = fopen(strTemp, "wb");
			fwrite(&W, sizeof(W), 1, FOUT);
			fwrite(&H, sizeof(H), 1, FOUT);
			fwrite(&i_dim, sizeof(i_dim), 1, FOUT);
			fwrite(&r_dim, sizeof(r_dim), 1, FOUT);

			float* FD = new float[WHR];
			const double* qdd = ResponseList.pElement;
			float* pfd = FD;
			size_t st_i;
			for ( st_i = 0; st_i < WHR; ++st_i )
				*(pfd++) = (float)(*(qdd++));
			fwrite(FD, sizeof(*FD), WHR, FOUT);
			delete[] FD;

			fclose(FOUT);
		}
	}

	if (pOutput != NULL)
	{
		const double* qd;
		double* pd = pOutput + ((size_t)WH) * (size_t)_OutputOffset;
		for ( i = 0; i < o_dim; ++i )
		{
			qd = ResponseList.pElement + WH * OutputIndexList.pElement[i];
			for ( j = 0; j < WH; ++j )
				*(pd++) = *(qd++);
		}
	}

	pFilterIndex = InputFilterIndexList.pElement;
	for ( i = 0; i < i_dim; ++i )
	{
		fi = *(pFilterIndex++);
		ppInputFilterListBase[fi]->_ReportedByChildren();
	}

	fIsCalculationDone = true;
	//_ReportedByChildren();
}

void CImageFilter::_ReportedByChildren()
{
	if (!fIsCalculationDone)
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		cout<<"ERROR!!!"<<endl;
		exit(0);
	}
	++_Amount_ReportGot;
	if (_Amount_ReportGot > _ResponseUserAmount)
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		cout<<"ERROR!!!"<<endl;
		exit(0);
	}
	if (_Amount_ReportGot == _ResponseUserAmount)
		ResponseList.DeepClear();

	//if (++_ReportedChildrenAmount < _ResponseUserAmount) return;
	//ClearContent();
}

// [*] CImageFilter_RGB

CImageFilter* CImageFilter_RGB::myClone() const
{
	CImageFilter_RGB* ret = new CImageFilter_RGB(*this);
	ret->Copy_IO_Settings(this);
	return ret;
}

int CImageFilter_RGB::GetResponseDimension()
{
	return 3;
}

void CImageFilter_RGB::_ComputeResponse(int W, int H, double** ppInputResponse, double* pResponse)
{
	//int i,j;
	//int WH = W * H;
	//const double* qd;

	//double* pd = pResponse;
	//for ( i = 0; i < 3; ++i )
	//{
	//	qd = ppInputResponse[i];
	//	for ( j = 0; j < WH; ++j )
	//		*(pd++) = *(qd++);
	//}
}

// [*] CImageFilterBank

void CImageFilterBank::myRelease()
{
	pBasicFilter_RGB = NULL;
	CDataStructureTools::ReleasePointers(OrderedFilterBank);
	_OutputDimension = 0;
}

CImageFilterBank::CImageFilterBank()
{
	OrderedFilterBank.Clear();
	myInit();
}

CImageFilterBank::~CImageFilterBank()
{
	myRelease();
}

CImageFilterBank::CImageFilterBank(const CImageFilterBank& AnotherInstance)
{
	OrderedFilterBank.Clear();

	myInit();
	pBasicFilter_RGB->Copy_IO_Settings(AnotherInstance.pBasicFilter_RGB);
	//pBasicFilter_RGB = (CImageFilter_RGB*)AnotherInstance.pBasicFilter_RGB->myClone();
	//OrderedFilterBank.PushBack(pBasicFilter_RGB);

	int i;
	for ( i = 1; i < (int)AnotherInstance.OrderedFilterBank.Size; ++i )
		this->PushBackFilter(AnotherInstance.OrderedFilterBank.pElement[i]->myClone());
	GetOutputDimension(true);

	this->strPN_Cache = AnotherInstance.strPN_Cache;

	ClearContent();
}

CImageFilterBank& CImageFilterBank::operator = (const CImageFilterBank& AnotherInstance)
{
	if (&AnotherInstance == this) return *this;

	myInit();
	pBasicFilter_RGB->Copy_IO_Settings(AnotherInstance.pBasicFilter_RGB);
	//pBasicFilter_RGB = (CImageFilter_RGB*)AnotherInstance.pBasicFilter_RGB->myClone();
	//OrderedFilterBank.PushBack(pBasicFilter_RGB);
	int i;
	for ( i = 1; i < (int)AnotherInstance.OrderedFilterBank.Size; ++i )
		this->PushBackFilter(AnotherInstance.OrderedFilterBank.pElement[i]->myClone());
	GetOutputDimension(true);

	strPN_Cache = AnotherInstance.strPN_Cache;

	this->ClearContent();

	return *this;
}

CImageFilterBank* CImageFilterBank::myClone() const
{
	return new CImageFilterBank(*this);
}

void CImageFilterBank::myInit()
{
	myRelease();

	//FilterComputedFlagList.Clear();
	pBasicFilter_RGB = new CImageFilter_RGB();
	PushBackFilter(pBasicFilter_RGB);

	//FlagUseOutputCacheAreaThresholdList.Clear();
	strPN_Cache = "";
}

size_t CImageFilterBank::GetMemoryUsage() const
{
	size_t ret = sizeof(int) * 4 + sizeof(CImageFilter_RGB*)
		+ OutputList.GetMemoryUsage()
		//+ FlagUseOutputCacheAreaThresholdList.GetMemoryUsage()
		+ FlagGotOutputValueList.GetMemoryUsage()
		+ OrderedFilterBank.GetMemoryUsage()
		//+ FilterComputedFlagList.GetMemoryUsage()
		+ FilterOutputStartOffsetList.GetMemoryUsage();
	int i;
	for ( i = 0; i < (int)OrderedFilterBank.Size; ++i )
		ret += OrderedFilterBank.pElement[i]->GetMemoryUsage();
	return ret;
}

void CImageFilterBank::SetCachePath(string strPN_Cache)
{
	this->strPN_Cache = strPN_Cache;
}

int CImageFilterBank::GetOutputDimension(bool fShouldRecompute)
{
	if (_OutputDimension>=0 && !fShouldRecompute) return _OutputDimension;

	int i;
	CImageFilter** ppFilter;

	FilterOutputStartOffsetList.Clear();
	_OutputDimension = 0;
	ppFilter = OrderedFilterBank.pElement;
	for ( i = 0; i < (int)OrderedFilterBank.Size; ++i )
	{
		FilterOutputStartOffsetList.PushBack(_OutputDimension);
		(*ppFilter)->SetOutputOffset(_OutputDimension);
		_OutputDimension += (*ppFilter)->GetOutputDimension();
		++ppFilter;
	}
	FilterOutputStartOffsetList.PushBack(_OutputDimension);

	FlagGotOutputValueList.Resize(_OutputDimension);
	FlagGotOutputValueList.Fill(false);

	return _OutputDimension;
}

void CImageFilterBank::PushBackFilter(CImageFilter* pImageFilter)
{
	int fi = (int)OrderedFilterBank.Size;
	pImageFilter->SetSelfIndex(fi);
	OrderedFilterBank.PushBack(pImageFilter);
	_OutputDimension = -1;

	//FilterComputedFlagList.PushBack(false);
}

const double* CImageFilterBank::GetOutput(int OutputIndex)
{
	if (OutputIndex < 0) return NULL;
	size_t offset = WH * OutputIndex;
	if (offset >= OutputList.Size) return NULL;

	if (!FlagGotOutputValueList.pElement[OutputIndex])
	{
		FlagGotOutputValueList.pElement[OutputIndex] = true;

		bool fCacheLoaded = false;

		if (!fCacheLoaded)
		{
			int i = (int)(upper_bound(FilterOutputStartOffsetList.Begin(), FilterOutputStartOffsetList.End(), OutputIndex) - FilterOutputStartOffsetList.Begin() - 1);
			if (i>0 && !(OrderedFilterBank.pElement[i]->fIsCalculationDone)) // !FilterComputedFlagList.pElement[i])
			{
				const char* ccpPN_Cache = strPN_Cache.c_str();
				const char* ccpFN_ImageHash = strFN_ImageCacheHashName.c_str();
				OrderedFilterBank.pElement[i]->Work(W, H, OrderedFilterBank.pElement, OutputList.pElement, ccpPN_Cache, ccpFN_ImageHash);
				//FilterComputedFlagList.pElement[i] = true;
			}
		}
	}

	return OutputList.pElement + offset;
}

void CImageFilterBank::ClearContent()
{
	ImageHandle = 0;
	WH = W = H = 0;
	this->strFN_ImageCacheHashName = "";
	OutputList.DeepClear();
	//FilterComputedFlagList.Fill(false);
	FlagGotOutputValueList.Fill(false);
	int i;
	for ( i = 0; i < (int)OrderedFilterBank.Size; ++i )
		OrderedFilterBank.pElement[i]->ClearContent();
}

